﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
using System.Collections;

namespace Framework.Constructor.Sql
{
    public static class Dao
    {
        public static Collection<BaseDeDatos> ObtenerBases(ConnectionStringSettings conexion)
        {
            Collection<BaseDeDatos> resultado = new Collection<BaseDeDatos>();
            Collection<Dictionary<String, Object>> dicionarios = Obtener( conexion.ConnectionString, "SELECT * from sys.databases", null );
            foreach ( Dictionary<String, Object> diccionario in dicionarios )
            {
                resultado.Add( new BaseDeDatos( diccionario ) );
            }
            return resultado;
        }

        private static Collection<Dictionary<String, Object>> Obtener( String conexion,  String query, Hashtable filtros)
        {
            Collection<Dictionary<String, Object>> resultado = new Collection<Dictionary<String, Object>>();
            using ( SqlConnection sqlConn = new SqlConnection( conexion ) )
            {
                using ( SqlCommand sqlComm = new SqlCommand() )
                {
                    try
                    {
                        sqlConn.Open();
                        DataSet dataSet = new DataSet();
                        sqlComm.Connection = sqlConn;
                        sqlComm.CommandText = ArmarQuery( query, filtros );
                        Collection<SqlParameter> parametros = ObtenerParametros( filtros );
                        foreach (SqlParameter p in parametros)
                        {
                            sqlComm.Parameters.Add( p );
                        }
                        SqlDataAdapter dataAdapter = new SqlDataAdapter( sqlComm );
                        dataAdapter.Fill( dataSet );
                        foreach ( DataTable dt in dataSet.Tables )
                        {
                            foreach ( DataRow dr in dt.Rows )
                            {
                                resultado.Add( ObtenerDicionario( dt, dr ) );
                            }
                        }
                        sqlConn.Close();
                    }
                    catch { }
                }
            }
            return resultado;
        }

        private static String ArmarQuery( string query, Hashtable filtros )
        {
            StringBuilder sb = new StringBuilder();
            sb.Append( query );
            if ( filtros != null && filtros.Count > 0 )
            {
                sb.Append( " WHERE " );
                foreach ( String key in filtros.Keys )
                {
                    sb.Append( String.Format( "{0}=@{0} ", key ) );
                }
            }
            sb.Append( " ORDER BY name" );
            return sb.ToString();
        }

        private static Collection<SqlParameter> ObtenerParametros( Hashtable filtros )
        {
            Collection<SqlParameter> resultado = new Collection<SqlParameter>();
            if (filtros!=null)
                foreach ( String key in filtros.Keys )
                {
                    resultado.Add( new SqlParameter( key, filtros[key] ) );
                }
            return resultado;
        }

        private static Dictionary<String, Object> ObtenerDicionario( DataTable dt, DataRow dr )
        {
            Dictionary<String, Object> resultado = new Dictionary<string, object>();
            foreach ( DataColumn dc in dt.Columns )
            {
                resultado.Add( dc.ColumnName, dr[dc.ColumnName] );
            }
            return resultado;
        }

        internal static Collection<Esquema> ObtenerEsquemas( ConnectionStringSettings conexion, BaseDeDatos baseDeDatos)
        {
            Collection<Esquema> resultado = new Collection<Esquema>();
            Hashtable filtros = new Hashtable();
            String conn = conexion.ConnectionString + " Database = " + baseDeDatos.Name;
            Collection<Dictionary<String, Object>> dicionarios = Obtener( conn, "select * from sys.schemas", filtros );
            foreach ( Dictionary<String, Object> diccionario in dicionarios )
            {
                resultado.Add( new Esquema( diccionario ) );
            }
            return resultado;
        }

        internal static Collection<Tabla> ObtenerTablas( ConnectionStringSettings conexion, BaseDeDatos baseDeDatos, Esquema esquema )
        {
            Collection<Tabla> resultado = new Collection<Tabla>();
            Hashtable filtros = new Hashtable();
            filtros.Add( "schema_id", esquema.Id );
            String conn = conexion.ConnectionString + " Database = " + baseDeDatos.Name;
            Collection<Dictionary<String, Object>> dicionarios = Obtener( conn, "select * from sys.tables", filtros );
            foreach ( Dictionary<String, Object> diccionario in dicionarios )
            {
                Collection<Columna> columns = ObtenerColumnas( conexion, baseDeDatos, esquema, new Tabla( diccionario ) );
                if ( columns != null )
                    diccionario.Add( "Columnas", columns );
                resultado.Add( new Tabla( diccionario ) );
            }
            return resultado;
        }

        internal static Collection<Vista> ObtenerVistas( ConnectionStringSettings conexion, BaseDeDatos baseDeDatos, Esquema esquema )
        {
            Collection<Vista> resultado = new Collection<Vista>();
            Hashtable filtros = new Hashtable();
            filtros.Add( "schema_id", esquema.Id );
            String conn = conexion.ConnectionString + " Database = " + baseDeDatos.Name;
            Collection<Dictionary<String, Object>> dicionarios = Obtener( conn, "select * from sys.views", filtros );
            foreach ( Dictionary<String, Object> diccionario in dicionarios )
            {
                resultado.Add( new Vista( diccionario ) );
            }
            return resultado; 
        }

        internal static Collection<Columna> ObtenerColumnas( ConnectionStringSettings conexion, BaseDeDatos baseDeDatos, Esquema esquema, Tabla tabla )
        {
            Collection<Columna> resultado = new Collection<Columna>();
            Hashtable filtros = new Hashtable();
            filtros.Add( "object_id", tabla.Id );
            String conn = conexion.ConnectionString + " Database = " + baseDeDatos.Name;
            Collection<Dictionary<String, Object>> dicionarios = Obtener( conn, "select * from sys.columns", filtros );
            foreach ( Dictionary<String, Object> diccionario in dicionarios )
            {
                Collection<TipoDeDato> tipos = ObtenerTipoDeDato( conexion, new Columna( diccionario ) );
                if (tipos!=null && tipos.Count>0)
                    diccionario.Add( "TipoDeDato", tipos[0] );
                resultado.Add( new Columna( diccionario ) );
            }
            return resultado;
        }

        internal static Collection<TipoDeDato> ObtenerTipoDeDato( ConnectionStringSettings conexion, Columna columna )
        {
            Collection<TipoDeDato> resultado = new Collection<TipoDeDato>();
            Hashtable filtros = new Hashtable();
            if ( columna != null )
                filtros.Add( "system_type_id", columna.System_Type_Id );
            String conn = conexion.ConnectionString; //+ " Database = " + baseDeDatos.Name;
            Collection<Dictionary<String, Object>> dicionarios = Obtener( conn, "select * from sys.types", filtros );
            foreach ( Dictionary<String, Object> diccionario in dicionarios )
            {
                resultado.Add( new TipoDeDato( diccionario ) );
            }
            return resultado;
        }

        internal static Collection<Procedimiento> ObtenerProcedimientos( ConnectionStringSettings conexion, BaseDeDatos baseDeDatos, Esquema esquema )
        {
            Collection<Procedimiento> resultado = new Collection<Procedimiento>();
            Hashtable filtros = new Hashtable();
            //filtros.Add( "schema_id", esquema.Id );
            String conn = conexion.ConnectionString + " Database = " + baseDeDatos.Name;
            Collection<Dictionary<String, Object>> dicionarios = Obtener( conn, "select * from sys.procedures", filtros );
            foreach ( Dictionary<String, Object> diccionario in dicionarios )
            {
                resultado.Add( new Procedimiento( diccionario ) );
            }
            return resultado; 
        }

        internal static String ObtenerDefinicionDelObjeto( ConnectionStringSettings conexion, BaseDeDatos baseDeDatos, Int32 id )
        {
            String resultado= String.Empty;
            String conn = conexion.ConnectionString + " Database = " + baseDeDatos.Name;
            using ( SqlConnection sqlConn = new SqlConnection( conn ) )
            {
                using ( SqlCommand sqlComm = new SqlCommand() )
                {
                    try
                    {
                        sqlConn.Open();
                        DataSet dataSet = new DataSet();
                        sqlComm.Connection = sqlConn;
                        sqlComm.CommandText = "select OBJECT_DEFINITION ("+id.ToString()+")";
                        resultado = sqlComm.ExecuteScalar().ToString();
                    }
                    catch { }
                }
            }
            return resultado;
        }


        internal static BaseDeDatos ObtenerBase( ConnectionStringSettings connectionStringSettings, string p )
        {
            BaseDeDatos resultado = null; 
            foreach ( BaseDeDatos item in ObtenerBases( connectionStringSettings ) )
            {
                if ( item.Name.ToLowerInvariant() == p.ToLowerInvariant() )
                {
                    resultado = item;
                    break;
                }
            }
            return resultado;
        }

        internal static Esquema ObtenerEsquema( ConnectionStringSettings connectionStringSettings, BaseDeDatos baseDeDatos, string p )
        {
            Esquema resultado = null;
            foreach ( Esquema item in ObtenerEsquemas( connectionStringSettings, baseDeDatos ) )
            {
                if ( item.Name.ToLowerInvariant() == p.ToLowerInvariant() )
                {
                    resultado = item;
                    break;
                }
            }
            return resultado;
        }
    }
}
